module("html_canvas", package.seeall)
require"png"
require"image_map"

STRATA_ORDER = {
	BACKGROUND = 1,
	LOW = 2,
	MEDIUM = 3,
	HIGH = 4,
	DIALOG = 5,
	FULLSCREEEN = 6,
	FULLSCREEN_DIALOG = 7,
	TOOLTIP = 8,
}

DRAWLAYER_ORDER = {
	BACKGROUND = 1,
	BORDER = 2,
	ARTWORK = 3,
	OVERLAY = 4,
	HIGHLIGHT = 5,
}

HEADER_P = [[
<html>
	<head>
		<script type="text/javascript" src="js/imageLoader.js"></script>
		<script type="text/javascript" src="js/frame.js"></script>
		<script type="text/javascript" src="js/backdrop.js"></script>
		<script type="text/javascript" src="js/texture.js"></script>
		<script type="text/javascript" src="js/fontstring.js"></script>
		<script type="text/javascript" src="js/rect.js"></script>
		<style type="text/css">
			@font-face {
				font-family: 'Friz Quadrata TT';
				src: url("Fonts/FRIZQT__.TTF");
				font-weight: bold;
				font-style: normal;
			}
		</style>
	</head>
	<body>
		<div>
			<input type="button" id="button-redraw" value="Redraw"></input>
			<input type="checkbox" id="checkbox-view-frame" checked="checked">View Frame</input>
			<input type="checkbox" id="checkbox-use-wowprogramming">Use Wowprogramming as image source
		</div>
		<canvas width="%d" height="%d" id="main-canvas"></canvas>
		<script type="text/javascript" src="js/main.js"></script>
		<script type="text/javascript">
]]

DIMENSION_P = [[%d, %d, %d, %d]]
COLOR_P = "#%02x%02x%02x"

FRAME_HEADER_P = [[
new Frame(RootWindow, %s, function () { // %s
	var region;
]]
FRAME_FOOTER = [[
});
]]

TEXTURE_P = [[
	region = new Texture("%s", "%s", %s); // %s
	region.setBlendMode("%s", %f);
	this.addRegion(region);
]]
SETTEXCOORD_P = [[
	region.setTexCoord(%d, %d, %d, %d);
]]
RECT_P = [[
	region = new Rect("%s", "%s", %f, %s);
	this.addRegion(region);
]]
FONTSTRING_P = [[
	region = new FontString("%s", "%s", %d, %s);
	region.setColor("%s");
	region.setText("%s");
	this.addRegion(region);
]]

BACKDROP_P = [[
	this.setBackdrop(new Backdrop(this, {
		bgFile: %s,
		edgeFile: %s,
		tile: %s,
		tileSize: %d,
		edgeSize: %d,
		insets: {
			left: %d,
			right: %d,
			top: %d,
			bottom: %d,
		},
	}));
]]

FOOTER_P = [=[
ImageLoader.setFinished();
		</script>
	</body>
</html>
]=]


function encode(s)
	if s and type(s) ~= "string" then
		s = tostring(s)
	end
	if not s then return end
	return s:replace("&", "&amp;"):replace("<", "&lt;"):replace('"', '&quot;'):replace("\\", "\\\\")
end

function encode_texture(s)
	if not s then return end
	s = s:replace("\\", "/")
	s = image_map[s]
	return encode(s)
end

function sort_regions(a, b)
	local la = assert(DRAWLAYER_ORDER[a[1]], "Unknown draw layer "..a[1])
	local lb = assert(DRAWLAYER_ORDER[b[1]], "Unknown draw layer "..b[1])
	if la == lb then
		return a[2] < b[2]
	else
		return la < lb
	end
end

function sort_frames(a, b)
	local sa = assert(STRATA_ORDER[a[1]], "Unknown strata "..a[1])
	local sb = assert(STRATA_ORDER[b[1]], "Unknown strata "..b[1])
	if sa == sb then
		return a[2] < b[2]
	else
		return sa < sb
	end
end

function get_dimension(f, base_height, accept_zero)
	local width, height, left = f:GetWidth(), f:GetHeight(), f:GetLeft()
	if not accept_zero and (width == 0 or height == 0) then return end
	local top = base_height - (f:GetBottom() + height)
	return left, top, width, height
end

local function get_color(f)
	local r, g, b = f:GetTextColor()
	return COLOR_P:format(r * 255, g * 255, b * 255)
end

local function get_texture(region)
	local texture
	if type(region) == "table" then
		texture = region.GetTexture and region:GetTexture()
	elseif type(region) == "string" then
		texture = region
	end
	if texture and texture:find("^UNIT:") then texture = "Interface\\CHARACTERFRAME\\TemporaryPortrait-Male-Human" end
	if not texture or texture:find(":", nil, true) then return end
	return encode_texture(texture..".png")
end

local function get_texture_js(s)
	local t = get_texture(s)
	return t and '"'..t..'"' or "null"
end



local Canvas = {
	__index = {
		open = function (self, file)
			self.file = file or "frames.html"
			self.frames = {}
			self.regions = {}
			self.fonts = {
				["Fonts\\FRIZQT__.TTF"] = "Friz Quadrata TT",
				["Fonts\\ARIALN.TTF"] = "Arial Narrow",
			}
			self.index = 0
		end,
		encode_font = function (self, fontname)
			local font = self.fonts[fontname]
			if not font then
				log("WW Unknown Font %s", fontname)
				return fontname
			end
			return font
		end,
		write_regions = function (self, o, regions)
			for _, data in ipairs(regions) do
				local layer, type, region = unpack(data)
				local dimension = DIMENSION_P:format(get_dimension(region, self.height, type == 2))
				if type == 1 then
					local texture = get_texture(region)
					local t = UI.params[region]
					if texture then
						o:write(TEXTURE_P:format(layer, texture, dimension, t.name or "nil", t.blend, t.alpha * t.vertex_color_a))
						local x, y, w, h = t.texcoordleft, t.texcoordtop, (t.texcoordright - t.texcoordleft), (t.texcoordbottom - t.texcoordtop)
						local t_width, t_height = png.getsize(texture)
						o:write(SETTEXCOORD_P:format(x * t_width, y * t_height, w * t_width, h * t_height))
					else
						local r, g, b, a
						if t.texture then
							r, g, b, a = string.match(t.texture, "COLOR{([%d.]+),([%d.]+),([%d.]+),([%d.]+)}")
						end
						if not r then
							r, g, b, a = 1, 1, 1, 1
						end
						local vr, vg, vb, va = region:GetVertexColor()
						local color = COLOR_P:format(r*vr*255, g*vg*255, b*vb*255);
						o:write(RECT_P:format(layer, color, a*va, dimension))
					end
				else
					local text = region.GetText and region:GetText()
					if text then
						local font, height, outline = region:GetFont()
						local left, top, width, height = get_dimension(region, self.height, true)
						font = self:encode_font(font)

						o:write(FONTSTRING_P:format(layer, font, height, DIMENSION_P:format(left, top + height, width, height), get_color(region), encode(text)))
					end
				end
			end
		end,
		write_frame_backdrop = function (self, o, f, id, left, top, width, height)
			local backdrop = f:GetBackdrop()
			if not backdrop then return end
			o:write(BACKDROP_P:format(
				get_texture_js(backdrop.bgFile),
				get_texture_js(backdrop.edgeFile),
				backdrop.tile and "true" or "false",
				backdrop.tileSize,
				backdrop.edgeSize,
				backdrop.insets.left,
				backdrop.insets.right,
				backdrop.insets.top,
				backdrop.insets.bottom))
		end,
		write_frame = function (self, o, id, f)
			o:write(FRAME_HEADER_P:format(DIMENSION_P:format(get_dimension(f, self.height)), f:GetName() or "anonymous"))
			self:write_frame_backdrop(o, f, id, get_dimension(f, self.height))
			local regions = self.regions[id]
			if regions then
				table.sort(regions, sort_regions)
				self:write_regions(o, regions)
			end
			o:write(FRAME_FOOTER)
		end,
		write_frames = function (self, o)
			local base_strata = nil
			for _, data in ipairs(self.frames) do
				self:write_frame(o, unpack(data, 3))
			end
		end,
		write = function (self)
			print("HTML5 CANVAS", self.file, #self.frames)
			local o = io.open(self.file, "w")
			o:write(HEADER_P:format(self.width, self.height))
			self:write_frames(o)
			o:write(FOOTER_P:format(
				self.width / 2, self.height / 2,
				self.width / 2, self.height / 2 + 28))
			o:close()
		end,
		close = function (self)
			table.sort(self.frames, sort_frames)
			self:write()
		end,
		add_regions = function (self, layers, ...)
			local regions = {}
			for i = 1, select("#", ...) do
				local region = select(i, ...)
				local rtype = region:IsObjectType("Texture") and 1 or 2
				if region:IsShown() and get_dimension(region, self.height, rtype == 2) then
					local layer = region:GetDrawLayer()
					if layers[layer] then
						regions[#regions + 1] = { layer, rtype, region,  }
					end
				end
			end
			return next(regions) and regions
		end,
		add = function (self, ...)
			for i = 1, select("#", ...) do
				local f = select(i, ...)
				local strata = assert(f:GetFrameStrata(), "No strata for "..(f:GetName() or ""))
				if f:IsShown() and get_dimension(f, self.height) and strata ~= "UNKNOWN" then
					local name = f:GetName()
					if not name then
						name = "_frame"..self.index
						self.index = self.index + 1
					end
					self.frames[#self.frames + 1] = { strata, f:GetFrameLevel(), name, f }
					self.regions[name] = self:add_regions(UI.params[f].layers, f:GetRegions()) -- FIXME ?? layers access
					self:add(f:GetChildren())
				end
			end
		end,
	},
}

function new(width, height)
	return setmetatable({width = width, height = height}, Canvas)
end
